﻿using System;
using System.Collections;
using Lextm.SharpSnmpLib;
using Snmp.Agent.DataLoader;

namespace Snmp.Agent.MplsLsrMib
{
    class Mib
    {
        public const string MplsLsrStdMibId = ".1.3.6.1.2.1.10.166.2.1";

        private const string MplsInSegmentTableId = ".4";
        private const string MplsOutSegmentTableId = ".7";
        private const string MplsXCTableId = ".10";
        private const string MplsLabelStackTableId = ".13";

        private const int MplsInSegmentTableAttrCount = 11;
        private const int MplsOutSegmentTableAttrCount = 12;
        private const int MplsXCTableAttrCount = 10;
        private const int MplsLabelStackTableAttrCount = 6;

        private static readonly int[] MplsInSegmentTableIdxAttrIdx = new[] { 0 };
        private static readonly int[] MplsOutSegmentTableIdxAttrIdx = new[] { 0 };
        private static readonly int[] MplsXCTableIdxAttrIdx = new[] { 0, 1, 2 };
        private static readonly int[] MplsLabelStackTableIdxAttrIdx = new[] { 0 };

        private SortedList mib = new SortedList(new IdStringComparer());

        public Mib()
        {
            //mplsInSegmentTable
            AddTable("mplsInSegmentTable", MplsInSegmentTableAttrCount, MplsInSegmentTableIdxAttrIdx, MplsInSegmentTableId);
            //mplsOutSegmentTable
            AddTable("mplsOutSegmentTable", MplsOutSegmentTableAttrCount, MplsOutSegmentTableIdxAttrIdx, MplsOutSegmentTableId);
            //mplsXCTable
            AddTable("mplsXCTable", MplsXCTableAttrCount, MplsXCTableIdxAttrIdx, MplsXCTableId);
            //mplsLabelStackTable
            //            Table mplsLabelStackTable = new Table(MplsLabelStackTableAttrCount, MplsLabelStackTableIdxAttrIdx,
            //                                                 MplsLsrStdMibId + "." + MplsLabelStackTableId);
            //            mplsInSegmentTable.Load(Loader.Load("mplsXCTable.xml"));
            //            mib.Add(MplsLabelStackTableId, new Table(MplsLabelStackTableAttrCount, MplsLabelStackTableIdxAttrIdx, MplsLsrStdMibId + "." + MplsLabelStackTableId));
        }

        private void AddTable(string tableName, int attrCount, int[] idxAttrIdx, string tableId)
        {
            var table = new Table(attrCount, idxAttrIdx,
                                                MplsLsrStdMibId + tableId);
            table.Load(Loader.Load(tableName + ".xml"));
            mib.Add(tableId, table);
        }

        public Variable GetRequest(string id)
        {
            Table targetTable = GetTargetTable(id);
            string tableRelativeId = GetTableRelativeId(id);

            var noSuchObjectResult = new Variable(new ObjectIdentifier(id), new NoSuchObject());

            return GetIdElems(tableRelativeId).Length < 3 ? noSuchObjectResult : targetTable.GetRequest(tableRelativeId);
        }

        public Variable GetNextRequest(string id)
        {
            Table targetTable = GetTargetTable(id);
            Variable result;
            if (targetTable == null)
            {
                targetTable = GetNextTable(id);
                id = targetTable.TableId;
            }

            if (targetTable == null)
            {
                //todo next table
                result = new Variable(new ObjectIdentifier(id), new EndOfMibView());
            }
            else
            {
                string tableRelativeId = GetTableRelativeId(id);
                result = targetTable.GetNextRequest(tableRelativeId);
                if (result == null)
                {
                    Table nextTable = GetNextTable(id);
                    if (nextTable == null)
                    {
                        result = new Variable(new ObjectIdentifier(id), new EndOfMibView());
                    }
                    else
                    {
                        result = nextTable.GetNextRequest("");
                        if (result == null)
                        {
                            result = new Variable(new ObjectIdentifier(id), new EndOfMibView());
                        }
                    }

                }
            }

            return result;
        }


        public Variable SetRequest(Variable variable)
        {
            string id = variable.Id.ToString();
            ISnmpData value = variable.Data;
            Table targetTable = GetTargetTable(id);
            string tableRelativeId = GetTableRelativeId(id);
            Variable resultVariable = targetTable.SetRequest(tableRelativeId, value);
            return resultVariable;

        }

        private string GetMibRelativeId(string id)
        {

            string mibRelativeId = null;
            if (id.Length >= MplsLsrStdMibId.Length)
            {
                mibRelativeId = id.Substring(MplsLsrStdMibId.Length);
            }
            return mibRelativeId;
        }

        private string GetTableRelativeId(string id)
        {
            string mibRelativeId = GetMibRelativeId(id);
            string tableRelativeId = null;
            if (mibRelativeId != null)
            {
                String tableId = GetTableId(id);
                tableRelativeId = mibRelativeId.Substring(tableId.Length);
            }
            return tableRelativeId;
        }

        private string GetTableId(string id)
        {
            string mibRelativeId = GetMibRelativeId(id);
            string tableId = null;
            if (mibRelativeId != null)
            {
                tableId = "." + GetIdElems(mibRelativeId)[0];
            }
            return tableId;
        }

        private Table GetTargetTable(string id)
        {
            Table targetTable = null;
            if (id.StartsWith(MplsLsrStdMibId))
            {
                targetTable = (Table)mib[GetTableId(id)];
            }
            return targetTable;
        }

        private Table GetNextTable(string id)
        {
            var comparer = new IdStringComparer();
            Table nextTable;

            if (comparer.Compare(MplsLsrStdMibId + mib.GetKey(0), id) == 1)
            {
                nextTable = (Table)mib.GetByIndex(0);
            }
            else if (comparer.Compare(MplsLsrStdMibId + mib.GetKey(mib.Count - 1), id) == -1)
            {
                nextTable = null;
            }
            else
            {
                int targetTableIdx = mib.IndexOfKey(GetTableId(id));
                int nextTableIdx = targetTableIdx + 1;
                nextTable = (Table)mib.GetByIndex(nextTableIdx);
            }

            return nextTable;
        }

        private string[] GetIdElems(string id)
        {
            string[] idElems = id.Split(new[] { '.' }, StringSplitOptions.RemoveEmptyEntries);
            return idElems;
        }
    }
}
